﻿using System;
using System.Net;
using System.Collections.Specialized;
using System.Collections.Generic;
using UMC.Data;
using UMC.Net;
using System.IO;
using System.Buffers;
using System.Text.Unicode;
using System.Buffers.Text;
using System.Linq;

namespace UMC.Web
{
    /// <summary>
    /// WebServlet处理
    /// </summary>
    public class WebServlet : UMC.Net.INetHandler
    {
        static WebMeta _AppConf = new WebMeta();
        public static WebMeta AppConf
        {
            get { return _AppConf; }

        }
        static WebServlet()
        {
            Data.Provider provider = Reflection.Configuration("assembly")?["WebResource"];
            AuthSecret = provider?["authSecret"];
        }

        protected static String AuthSecret;
        protected void Temp(UMC.Net.NetContext context)
        {
            var file = UMC.Data.Utility.FilePath(context.Url.LocalPath.Substring(context.Url.LocalPath.IndexOf('/', 2) + 1));
            file = UMC.Data.Reflection.ConfigPath(String.Format("Static\\TEMP\\{0}", file));
            if (context.ContentLength > 0)
            {

                if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(file)))
                {
                    System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(file));
                }
                if (System.IO.File.Exists(file))
                {
                    System.IO.File.Delete(file);
                }

                System.IO.Stream sWriter = System.IO.File.Open(file, FileMode.Create);
                context.ReadAsData((b, i, c) =>
                {
                    if (c == 0 && b.Length == 0)
                    {
                        if (i == -1)
                        {
                            sWriter.Close();
                            System.IO.File.Delete(file);

                        }
                        else
                        {
                            sWriter.Close();
                            sWriter.Dispose();
                        }
                        context.OutputFinish();
                    }
                    else
                    {
                        sWriter.Write(b, i, c);
                    }
                });
            }
            else
            {
                File(context, file);
            }
        }
        void File(UMC.Net.NetContext context, String name)
        {
            if (System.IO.File.Exists(name))
            {
                TransmitFile(context, name, true);
            }
            else
            {
                context.StatusCode = 404;
            }
        }
        protected virtual void TransmitFile(UMC.Net.NetContext context, String file, bool isCache)
        {
            var lastIndex = file.LastIndexOf('.');

            if (lastIndex > -1 && lastIndex > file.LastIndexOf('/'))
            {
                var fType = UMC.Data.Reflection.Instance().MimeType(file.Substring(lastIndex + 1).ToLower());
                if (String.IsNullOrEmpty(fType))
                {
                    context.ContentType = StringValue.ContentTypeStream;
                }
                else
                {
                    context.ContentType = context.Memory.Rent(fType);
                }
            }
            else
            {
                context.ContentType = StringValue.ContentTypeStream;
            }
            var attachment = context.QueryString.Get("file");
            var fileInfo = new System.IO.FileInfo(file);
            var Modified = context.Memory.Rent(fileInfo.LastWriteTimeUtc.ToString("r"));
            if (String.IsNullOrEmpty(attachment) == false)
            {
                context.AddHeader(new HeadValue(StringValue.ContentDisposition, context.Memory.Rent($"attachment;filename={Uri.EscapeDataString(attachment)}"), MimeHeadKey.ContentDisposition));
            }
            else
            {
                context.AddHeader(new HeadValue(StringValue.LastModified, Modified, MimeHeadKey.LastModified));
                if (isCache)
                {
                    context.AddHeader(StringValue.CacheControl, "public");
                    var Since = context.Headers.Get("If-Modified-Since"u8);
                    if (Since.IsEmpty() == false)
                    {

                        if (Modified.Span.SequenceEqual(Since.Span))
                        {
                            context.StatusCode = 304;
                            return;

                        }
                    }
                    // context.AddHeader(StringValue.XCacheKey, Utility.TimeSpan(fileInfo.LastWriteTimeUtc).ToString());

                }
            }
            var range = context.Headers.Get("Range"u8);

            if (range.IsEmpty() == false)
            {
                var IfSince = context.Headers.Get("If-Range"u8);
                if (range.IsEmpty() == false)
                {
                    if (IfSince.Span.SequenceEqual(Modified.Span) == false)
                    {
                        range = StringValue.Empty;
                    }
                }
            }
            using (System.IO.FileStream stream = System.IO.File.OpenRead(file))
            {

                if (range.IsEmpty == false && range.Span.StartsWith("bytes="u8) && context.StatusCode == 200)
                {
                    var rg = range.Span.Slice(6).UTF8().Trim().Split(',')[0];

                    int start = 0, len = -1;
                    if (rg.StartsWith("-"))
                    {
                        start = UMC.Data.Utility.IntParse(rg, 0);
                        len = Math.Abs(start);
                    }
                    else if (rg.EndsWith("-"))
                    {
                        start = UMC.Data.Utility.IntParse(rg.Substring(0, rg.Length - 1), 0);
                    }
                    else
                    {
                        var rs = rg.Split('-');
                        start = UMC.Data.Utility.IntParse(rs[0], 0);
                        len = UMC.Data.Utility.IntParse(rs[1], 0) - start + 1;
                    }
                    if (start < 0)
                    {
                        start = (int)stream.Seek(start, SeekOrigin.End);
                    }
                    else if (start < stream.Length)
                    {
                        stream.Seek(start, SeekOrigin.Begin);

                    }
                    var endLen = (int)(stream.Length - start);
                    if (len == -1 || len > endLen)
                    {
                        len = endLen;
                    }
                    if (len > 0)
                    {
                        context.StatusCode = 206;
                        context.AddHeader(StringValue.ContentRange, $"bytes {start}-{start + len - 1}/{stream.Length}");

                        context.ContentLength = len;
                        int count2 = 0;

                        byte[] array = ArrayPool<byte>.Shared.Rent(1024);
                        try
                        {
                            while (len > 0)
                            {
                                count2 = stream.Read(array, 0, array.Length);

                                if (count2 == 0)
                                {
                                    break;
                                }
                                else if (count2 > len)
                                {
                                    context.OutputStream.Write(array, 0, len);
                                    break;
                                }
                                else
                                {
                                    context.OutputStream.Write(array, 0, count2);
                                }
                                len -= count2;
                            }
                        }
                        finally
                        {
                            ArrayPool<byte>.Shared.Return(array);
                        }
                    }
                    else
                    {
                        context.ContentType = StringValue.ContentTypeText;
                        context.StatusCode = 416;

                        context.Output.Write($"416 {UMC.Net.HttpStatusDescription.Get(416)}");
                    }

                }
                else
                {
                    context.AddHeader(StringValue.AcceptRanges, "bytes");

                    context.ContentLength = stream.Length;
                    stream.CopyTo(context.OutputStream);
                    // int count;
                    // while ((count = stream.Read(array, 0, array.Length)) > 0)
                    // {
                    //     context.OutputStream.Write(array, 0, count);
                    // }
                }

            }

        }

        protected virtual void Process(UMC.Net.NetContext context)
        {
            if (context.UserAgent.IsEmpty)
            {
                return;
            }
            string model = String.Empty, cmd = String.Empty;
            var path = context.Path.Slice(1);
            if (path.Length > 0)
            {
                var v = path.Span.IndexOf((byte)'/');
                if (v > 0)
                {
                    path = path.Slice(v + 1);
                }
                else
                {
                    path = StringValue.Empty;
                }
            }

            var spIndex = path.Span.IndexOf((byte)'/');
            if (path.IsEmpty == false)
            {
                if (path.IsEmpty == false && spIndex == -1)
                {
                    context.Cookies["device"u8] = path;//[0];
                    path = StringValue.Empty;

                }
                else
                {
                    model = path.Slice(0, spIndex).UTF8();
                    path = path.Slice(spIndex + 1);
                    spIndex = path.Span.IndexOf((byte)'/');
                    if (path.IsEmpty == false && spIndex == -1)
                    {
                        cmd = path.UTF8();
                        path = StringValue.Empty;
                    }
                    else
                    {
                        cmd = path.Slice(0, spIndex).UTF8();
                        path = path.Slice(spIndex + 1);
                    }

                }
            }
            if (context.Token == null)
            {
                context.Token = this.AccessToken(context);
            }
            context.UseSynchronousIO();
            context.ReadAsForm(r =>
            {
                if (r?.Count > 0)
                {
                    Process(context, model, cmd, path, r, context.QueryString);
                }
                else
                {
                    Process(context, model, cmd, path, context.QueryString);
                }
            });
        }


        public static StringValue SessionCookieName = new StringValue("device");
        String Unescape(String u)
        {
            if (String.IsNullOrEmpty(u))
            {
                return u;
            }
            // Uri.d
            return Uri.UnescapeDataString(u);
        }
        private class ItmeToken : Security.AccessToken
        {
            public ItmeToken(UMC.Net.NetContext c, Guid guid) : base(guid)
            {
                this.context = c;

                var itme = c.Headers.Get("itme-access-token"u8);
                if (itme.IsEmpty == false)
                {
                    var decodedBytes = ArrayPool<byte>.Shared.Rent(Base64.GetMaxDecodedFromUtf8Length(itme.Span.Length));

                    Base64.DecodeFromUtf8(itme.Span, decodedBytes, out int inputDecodedBytes, out int numDecodedBytes);

                    if (numDecodedBytes > 0)
                    {
                        var JS = JSON.Deserialize<WebMeta>(decodedBytes.AsSpan(0, numDecodedBytes).UTF8());
                        var em = JS.GetDictionary().GetEnumerator();
                        while (em.MoveNext())
                        {
                            this.Items.Put(em.Key as string, em.Value as string);
                        }
                    }
                }
            }
            UMC.Net.NetContext context;
            public override void Commit(string deviceType, bool unqiue, string clientIP, string server)
            {
                base.Commit(deviceType, unqiue, clientIP, server);
                context.AddHeader("itme-access-token", Convert.ToBase64String(JSON.Serialize(this.Items).UTF8()));
            }
        }
        protected virtual UMC.Security.AccessToken AccessToken(UMC.Net.NetContext context)
        {
            // context.OutputHeaded

            var name = context.Headers.Get("umc-request-user-name"u8);
            var devices = context.Cookies.GetValues("device"u8);//.FirstOrDefault();// : new String[] { context.Device };

            if (name.IsEmpty || String.IsNullOrEmpty(AuthSecret) || Data.Utility.CheckSign(context.Headers, "umc-"u8, AuthSecret) == false)
            {
                if (devices.Length == 0)
                {
                    return new Security.AccessToken(Guid.Empty).Login(new UMC.Security.Guest(Guid.Empty), 0);
                }
                else
                {
                    var idd = Utility.Guid(devices[devices.Length - 1].Span.UTF8(), true) ?? Guid.Empty;
                    return new Security.AccessToken(idd).Login(new UMC.Security.Guest(idd), 0);
                }

            }
            var sName = name.Span.UTF8();
            var organizes = Unescape(context.Headers.Get("umc-request-user-organizes"u8).Span.UTF8());
            var roles = Unescape(context.Headers.Get("umc-request-user-roles"u8).Span.UTF8());
            var id = context.Headers.Get("umc-request-user-id"u8).Span.UTF8();
            var alias = Unescape(context.Headers.Get("umc-request-user-alias"u8).Span.UTF8());
            var user = UMC.Security.Identity.Create(Utility.Guid(id) ?? Utility.Guid(sName, true).Value, sName, alias
                , String.IsNullOrEmpty(roles) ? new String[0] : roles.Split(',')
                , String.IsNullOrEmpty(organizes) ? new String[0] : organizes.Split(','));
            ItmeToken token;
            if (devices.Length == 0)
            {
                token = new ItmeToken(context, new Guid(UMC.Data.Utility.MD5("umc.request", id, name.ToString(), roles, organizes, alias)));
            }
            else
            {
                token = new ItmeToken(context, Utility.Guid(devices[devices.Length - 1].Span.UTF8(), true).Value);

            }
            token.Login(user, 0);

            return token;



        }
        protected void Process(Net.NetContext context, params HeadValues[] nvs)
        {
            string model = String.Empty, cmd = String.Empty;
            var path = context.Path.Slice(1);
            if (path.Length > 0)
            {
                var v = path.Span.IndexOf((byte)'/');
                if (v > 0)
                {
                    path = path.Slice(v + 1);
                }
                else
                {
                    path = StringValue.Empty;
                }
            }

            var spIndex = path.Span.IndexOf((byte)'/');
            if (path.IsEmpty == false)
            {
                if (path.IsEmpty == false && spIndex == -1)
                {
                    context.Cookies["device"u8] = path;//[0];

                    Process(context, model, cmd, StringValue.Empty, nvs);
                }
                else
                {
                    model = path.Slice(0, spIndex).UTF8();
                    path = path.Slice(spIndex + 1);
                    spIndex = path.Span.IndexOf((byte)'/');
                    if (path.IsEmpty == false && spIndex == -1)
                    {
                        cmd = path.UTF8();

                        Process(context, model, cmd, StringValue.Empty, nvs);
                    }
                    else
                    {
                        cmd = path.Slice(0, spIndex).UTF8();
                        path = path.Slice(spIndex + 1);
                        Process(context, model, cmd, path, nvs);
                    }

                }

            }
            else
            {
                Process(context, model, cmd, StringValue.Empty, nvs);
            }

        }
        protected void Process(Net.NetContext context, String model, String cmd, StringValue pathValue, params HeadValues[] nvs)
        {
            if (context.Headers.TryGet("Origin"u8, out var Referer) && Referer.Value.IsEmpty == false)
            {
                context.AddHeader(StringValue.AccessControlAllowOrigin, Referer.Value);
            }
            else
            {
                context.AddHeader(StringValue.AccessControlAllowOrigin, "*");
            }

            context.AddHeader(StringValue.AccessControlAllowCredentials, "true");
            context.AddHeader(StringValue.CacheControl, "no-cache");
            var queryString = new NameValueCollection();
            var path = pathValue.Span;
            if (path.IsEmpty == false)
            {
                while (path.Length > 0)
                {

                    var spIndex = path.IndexOf((byte)'/');
                    if (spIndex > -1)
                    {
                        queryString.Add("_", path.Slice(0, spIndex).UTF8());

                        path = path.Slice(spIndex + 1);
                    }
                    else
                    {

                        queryString.Add("_", path.UTF8());
                        path = Span<byte>.Empty;
                    }
                }
            }
            bool IsContent = false;
            foreach (var nv in nvs)
            {
                foreach (var kv in nv.Span)
                {
                    var key = kv.Name.IsEmpty() ? "_" : kv.Name.UTF8();
                    String value = String.Empty;///
                    if (nv.IsUrlEncoded)
                    {
                        kv.Value.Output((b, i, c) => value = WebUtility.UrlDecodeToBytes(b, i, c).UTF8());
                    }
                    else
                    {
                        value = kv.Value.UTF8();
                    }
                    switch (key ?? "_")
                    {
                        case "_":
                            if (kv.Value.IsEmpty() == false)
                            {
                                queryString.Add("_", value);
                            }
                            break;
                        case "_cmd":
                            cmd = value;
                            break;
                        case "_model":
                            model = value;
                            break;
                        case "__":
                            if (kv.Value.IsEmpty() == false)
                            {
                                foreach (var pv in value.Split('/', StringSplitOptions.RemoveEmptyEntries))
                                {
                                    queryString.Add("_", pv);
                                }
                            }
                            break;
                        case "___":
                            IsContent = true;
                            break;
                        default:
                            if (!key.StartsWith("_"))
                            {
                                queryString.Add(key, value);
                            }
                            break;
                    }
                }
            }

            if (String.IsNullOrEmpty(model))
            {
                context.StatusCode = 404;
                context.ContentType = StringValue.ContentTypeText;
                context.Output.Write("Model is empty");
                context.OutputFinish();
            }
            else if (String.IsNullOrEmpty(cmd))
            {
                if (model.StartsWith("[") && model.EndsWith("]"))
                {
                    context.Token = this.AccessToken(context);
                    var client = new WebClient(context);
                    client.Command(model);
                }
                else
                {
                    context.StatusCode = 404;
                    context.ContentType = StringValue.ContentTypeText;
                    context.Output.Write("Command is empty");
                    context.OutputFinish();
                }

            }
            else
            {
                if (context.Token == null)
                {
                    context.Token = this.AccessToken(context);
                }

                var client = new WebClient(context);
                client.IsContent = IsContent;
                client.Command(model, cmd, queryString);

            }

        }

        protected void LocalResources(NetContext context, String path, bool check)
        {
            var dir = Reflection.ConfigPath("Static");
            var file = UMC.Data.Utility.FilePath(dir + path);
            if (check)
            {
                if (System.IO.File.Exists(file))
                {
                    TransmitFile(context, file, true);

                    context.OutputFinish();

                    return;
                }
            }
            var url = new Uri($"https://res.apiumc.com{path}?v{UMC.Data.Utility.TimeSpan()}");

            context.UseSynchronousIO();

            url.WebRequest(context.Memory).Get(xhr =>
            {
                if (xhr.StatusCode == HttpStatusCode.OK)
                {
                    var ms = UMC.Data.Utility.Writer(file, false);
                    xhr.ReadAsData((b, i, c) =>
                    {
                        if (c == 0 && b.Length == 0)
                        {
                            if (i == -1)
                            {
                                ms.Close();
                                ms.Dispose();

                                context.StatusCode = 404;
                                var lastIndex = file.LastIndexOf('.');
                                var extName = "html";
                                if (lastIndex > -1)
                                {
                                    extName = file.Substring(lastIndex + 1);
                                }
                                NotFound(context, extName, dir);

                                context.OutputFinish();
                            }
                            else
                            {
                                ms.Flush();
                                ms.Close();

                                TransmitFile(context, file, true);
                                context.OutputFinish();

                            }
                        }
                        else
                        {
                            ms.Write(b, i, c);
                        }
                    });
                }
                else
                {
                    context.StatusCode = 404;
                    var lastIndex = file.LastIndexOf('.');
                    var extName = "html";
                    if (lastIndex > -1)
                    {
                        extName = file.Substring(lastIndex + 1);
                    }
                    NotFound(context, extName, dir);

                    context.OutputFinish();

                }
            });
        }
        #region IMIMEHandler Members
        private void Conf(UMC.Net.NetContext context)
        {
            if (context.Method == HttpMethod.GET)//.Span.SequenceEqual(Mime.GET))
            {

                context.AddHeader(StringValue.CacheControl, "no-store");
                context.ContentType = StringValue.ContentTypeJS;
                context.Output.Write("UMC.UI.Config({possrc:'/',posurl: '");
                context.Output.Write("/UMC/");
                if (context.Cookies.GetValues("device"u8).Length == 0)
                {
                    context.AddHeader(StringValue.SetCookie, $"{SessionCookieName}={Utility.Guid(Utility.NewGuid())}; HttpOnly; Path=/");
                }

                context.Output.Write("'");
                context.Output.Write("});");
                return;
            }
            else
            {
                context.UseSynchronousIO();
                context.ReadAsStream(ms =>
                {
                    var rnew = new System.IO.StreamReader(ms);

                    var args = UMC.Data.JSON.Deserialize<Web.WebMeta>(rnew.ReadToEnd());

                    if (String.IsNullOrEmpty(AuthSecret))
                    {
                        var authSecret = args["authSecret"];
                        if (String.IsNullOrEmpty(authSecret))
                        {
                            UMC.Data.JSON.Serialize(new WebMeta().Put("code", "unactivated", "msg", "未激活").Put("appConf", _AppConf), context.Output);
                        }
                        else
                        {
                            AuthSecret = authSecret;
                            var provider = Web.WebResource.Instance().Provider;
                            provider.Attributes["authSecret"] = authSecret;

                            var domain = args["domain"];
                            if (String.IsNullOrEmpty(domain) == false)
                            {
                                provider.Attributes["domain"] = domain;
                            }
                            var config = Reflection.Configuration("assembly");
                            config.Add(provider);
                            Reflection.Configuration("assembly", config);

                            UMC.Data.JSON.Serialize(new WebMeta().Put("code", "success").Put("appConf", _AppConf), context.Output);
                        }
                    }
                    else
                    {
                        var appId = args["appId"];
                        var time = args["time"] as string;
                        var nvs = new NameValueCollection
                        {
                        { "appId", appId },
                        { "time", time }
                        };

                        if (String.Equals(args["sign"] as string, UMC.Data.Utility.Sign(nvs, AuthSecret)))
                        {

                            UMC.Data.JSON.Serialize(new WebMeta().Put("code", "success").Put("appConf", _AppConf), context.Output);

                        }
                        else
                        {

                            UMC.Data.JSON.Serialize(new WebMeta().Put("code", "error").Put("appConf", _AppConf), context.Output);
                        }
                    }
                    context.OutputFinish();

                });
            }

        }
        // override b
        public virtual void ProcessRequest(UMC.Net.NetContext context)
        {
            var pathValue = context.Path.Span.UTF8();

            var paths = new List<string>(pathValue.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries));
            if (paths.Count == 0)
            {
                paths.Add("index.html");
            }


            switch (paths[0])
            {
                case "TEMP":
                    Temp(context);
                    return;
                case "UMC":
                    var query = context.Query;
                    if (query.Contains("?_model="u8) || query.Contains("&_model="u8))
                    {
                        Process(context);
                        return;
                    }
                    else if (paths.Count == 2)
                    {
                        switch (paths[1])
                        {
                            case "Conf":
                                Conf(context);
                                return;
                            default:
                                if (context.Method == HttpMethod.GET)
                                {
                                    IndexResource(context);
                                }
                                else
                                {
                                    Process(context);

                                }
                                return;
                        }
                    }
                    else
                    {
                        switch (context.Method)
                        {
                            case HttpMethod.GET:

                                switch (paths.Count)
                                {
                                    case 2:
                                    case 1:
                                        IndexResource(context);
                                        break;
                                    case 3:
                                        if (pathValue.EndsWith("/"))
                                        {
                                            PageResource(context);
                                        }
                                        else
                                        {

                                            IndexResource(context);
                                        }
                                        break;
                                    default:
                                        if (pathValue.EndsWith("/"))
                                        {

                                            PageResource(context);
                                        }
                                        else
                                        {

                                            Process(context);
                                        }
                                        break;
                                }
                                break;
                            case HttpMethod.OPTIONS:
                                context.AddHeader(StringValue.AccessControlAllowOrigin, "*");
                                context.AddHeader(StringValue.AccessControlAllowCredentials, "Access-Control-Allow-Credentials");
                                context.AddHeader(StringValue.CacheControl, "no-cache");
                                break;
                            default:
                                Process(context);
                                break;
                        }
                        return;
                    }

                case "UMC.Conf":
                    Conf(context);
                    return;
                case "UMC.UI":
                    LocalResources(context, "/" + pathValue.Substring(5), true);
                    return;
                case "css":
                case "js":
                    LocalResources(context, pathValue, true);
                    return;
                case "log":
                    context.Token = this.AccessToken(context);
                    if (context.Token.IsInRole(Security.AccessToken.AdminRole))
                    {
                        StaticFile(Reflection.ConfigPath("Static"), context, pathValue);
                    }
                    else
                    {
                        context.Redirect("/");
                    }
                    break;
                default:
                    StaticFile(Reflection.ConfigPath("Static"), context, pathValue);
                    break;

            }



        }
        protected void StaticFile(string dir, Net.NetContext context, String path)
        {
            var file = UMC.Data.Utility.FilePath(dir + path);

            try
            {
                if (System.IO.File.Exists(file))
                {
                    TransmitFile(context, file, true);
                    return;
                }
                var extNameIndex = path.IndexOf('.', path.LastIndexOf('/'));
                var extName = "html";

                if (extNameIndex == -1)
                {

                    var staticFile = UMC.Data.Utility.FilePath(file + "/index.html");

                    if (System.IO.File.Exists(staticFile))
                    {
                        TransmitFile(context, staticFile, true);
                        return;

                    }
                }
                else
                {

                    extName = path.Substring(extNameIndex + 1);
                }
                NotFound(context, extName, dir);

            }
            finally
            {


                context.OutputFinish();

            }
        }
        protected virtual void NotFound(Net.NetContext context, string extName, string dir)
        {

            //SecFetchDest
            var fok404 = UMC.Data.Utility.FilePath($"{dir}/404.OK.{extName}");
            if (System.IO.File.Exists(fok404))
            {
                context.StatusCode = 200;
                TransmitFile(context, fok404, false);
            }
            else
            {
                var f404 = UMC.Data.Utility.FilePath($"{dir}/404.{extName}");
                context.StatusCode = 404;
                if (System.IO.File.Exists(f404))
                {
                    TransmitFile(context, f404, false);
                }
                else
                {
                    context.StatusCode = 404;
                    switch (extName)
                    {
                        case "html":
                            if (context.Path.Span.SequenceEqual("/"u8))
                            {
                                context.Redirect("/UMC");
                            }
                            else
                            {
                                context.Output.Write("Not Found");
                            }
                            break;
                        default:
                            var dest = context.Headers[StringValue.SecFetchDest];
                            if (dest.Span.SequenceEqual("image"u8))
                            {
                                f404 = UMC.Data.Utility.FilePath($"{dir}/favicon.ico");
                                if (System.IO.File.Exists(f404))
                                {
                                    TransmitFile(context, f404, false);
                                    break;
                                }
                            }


                            f404 = UMC.Data.Utility.FilePath($"{dir}/404.html");
                            if (System.IO.File.Exists(f404))
                            {
                                TransmitFile(context, f404, false);
                            }
                            else
                            {
                                context.Output.Write("Not Found");
                            }

                            break;
                    }
                }
            }
        }
        protected virtual void PageResource(Net.NetContext context)
        {
            context.ContentType = StringValue.ContentTypeHtml;
            using (System.IO.Stream stream = typeof(WebServlet).Assembly
                               .GetManifestResourceStream("UMC.Resources.page.html"))
            {
                context.ContentLength = stream.Length;
                stream.CopyTo(context.OutputStream);

            }
        }
        protected virtual void IndexResource(Net.NetContext context)
        {
            context.ContentType = StringValue.ContentTypeHtml;//"text/html";


            using (System.IO.Stream stream = typeof(WebServlet).Assembly
                               .GetManifestResourceStream("UMC.Resources.umc.html"))
            {
                context.ContentLength = stream.Length;
                stream.CopyTo(context.OutputStream);

            }
        }

        #endregion
        internal static List<WebMeta> Mapping()
        {

            List<WebMeta> metas = new List<WebMeta>();
            if (WebRuntime.webFactorys.Count > 0)
            {
                foreach (var wt in WebRuntime.webFactorys)
                {
                    var t = wt.Type;
                    WebMeta meta = new WebMeta();
                    meta.Put("type", t.FullName);
                    meta.Put("name", "." + t.Name);
                    metas.Add(meta);

                    ApiumcAttribute mapping = (ApiumcAttribute)t.GetCustomAttributes(typeof(ApiumcAttribute), false)[0];
                    if (String.IsNullOrEmpty(mapping.Desc) == false)
                    {
                        meta.Put("desc", mapping.Desc);

                    }

                }

            }
            if (WebRuntime.flows.Count > 0)
            {
                var em = WebRuntime.flows.GetEnumerator();
                while (em.MoveNext())
                {
                    var tls = em.Current.Value;
                    foreach (var wt in tls)
                    {
                        var t = wt.Type;
                        var auth = WebRuntime.authKeys[em.Current.Key];
                        WebMeta meta = new WebMeta();
                        meta.Put("type", t.FullName);
                        meta.Put("name", em.Current.Key + ".");
                        meta.Put("auth", auth.Item1.ToString().ToLower());
                        if (auth.Item2)
                        {
                            meta.Put("biometric", auth.Item2);
                        }
                        meta.Put("model", em.Current.Key);
                        metas.Add(meta);

                        var mappings = t.GetCustomAttributes(typeof(ApiumcAttribute), false);

                        ApiumcAttribute mapping = (ApiumcAttribute)mappings[0];
                        if (mappings.Length > 1)
                        {
                            foreach (var m in mappings)
                            {
                                var c = m as ApiumcAttribute;
                                if (String.Equals(c.Model, em.Current.Key))
                                {
                                    mapping = c;
                                    break;
                                }
                            }
                        }
                        if (String.IsNullOrEmpty(mapping.Desc) == false)
                        {
                            meta.Put("desc", mapping.Desc);

                        }

                    }


                }
            }
            if (WebRuntime.activities.Count > 0)
            {
                var em = WebRuntime.activities.GetEnumerator();
                while (em.MoveNext())
                {
                    var em3 = em.Current.Value.GetEnumerator();
                    while (em3.MoveNext())
                    {
                        var mappings = em3.Current.Value.Type.GetCustomAttributes(typeof(ApiumcAttribute), false);
                        ApiumcAttribute mapping = (ApiumcAttribute)mappings[0];
                        if (mappings.Length > 1)
                        {
                            foreach (var m in mappings)
                            {
                                var c = m as ApiumcAttribute;
                                if (String.Equals(c.Model, em.Current.Key) && String.Equals(c.Command, em3.Current.Key))
                                {
                                    mapping = c;
                                    break;
                                }
                            }
                        }

                        WebAuthType authType = mapping.Auth;

                        WebMeta meta = new WebMeta();
                        meta.Put("type", em3.Current.Value.Type.FullName);
                        meta.Put("name", em.Current.Key + "." + em3.Current.Key);
                        meta.Put("auth", authType.ToString().ToLower());
                        if (mapping.IsBiometric)
                            meta.Put("biometric", mapping.IsBiometric);
                        meta.Put("model", mapping.Model);
                        meta.Put("cmd", mapping.Command);
                        metas.Add(meta);

                        if (String.IsNullOrEmpty(mapping.Desc) == false)
                        {
                            meta.Put("desc", mapping.Desc);

                        }


                    }



                }
            }
            return metas;
        }

    }
}